/*
 * Copyright (C) 2008 Peter Kling
 * 
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 * 
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 'A Kind of Billiard'. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package de.akob.billiard;

import java.util.ArrayList;
import java.util.Collection;

import de.akob.mathematics.MathVector;
import de.akob.physics.Physics;

/**
 * This class collects some constants and global methods concerning the billiard game. Note some assumptions that are
 * made throughout the cg2.billiard package:
 * <p>
 * - table length corresponds to x-axis (left --> right)<p>
 * - table width corresponds to z-axis (back --> front)<p>
 * - table height correspond to y-axis (bottom --> up)<p>
 * - table is centered at (0, height, 0)
 */
public final class Billiard {
	/*
	 * Constants for pool billiard tables according to the DBU (Deutsche Billard-Union e.V.).
	 * 
	 * @see http://dbu.billardmanager.de/
	 */
	public static final double DBU_POOLTABLE_8FEET_LENGTH          = 2.2400;
	public static final double DBU_POOLTABLE_8FEET_WIDTH           = 1.1200;
	public static final double DBU_POOLTABLE_8FEET_HEIGHT          = 0.8000;
	public static final double DBU_POOLTABLE_8FEET_DIAMONDDISTANCE = 0.2800;
	public static final double DBU_POOLTABLE_8FEET_POCKETRADIUS    = 0.0850;
	public static final double DBU_POOLTABLE_9FEET_LENGTH          = 2.5400;
	public static final double DBU_POOLTABLE_9FEET_WIDTH           = 1.2700;
	public static final double DBU_POOLTABLE_9FEET_HEIGHT          = 0.8000;
	public static final double DBU_POOLTABLE_9FEET_DIAMONDDISTANCE = 0.3175;
	
	/*
	 * Constants for pool billiard balls according to the DBU (Deutsche Billard-Union e.V.).
	 * 
	 * @see http://dbu.billardmanager.de/
	 */
	public static final double DBU_POOLBALL_MASS                   = 0.1700;
	public static final double DBU_POOLBALL_RADIUS                 = 0.0286;
	
	/*
	 * Friction coefficient for billiard balls on standard cloth (empirical result).
	 */
	public static final double BILLIARDBALL_FRICTIONCOEFFICIENT    = 0.02;
	
	/*
	 * Names of texture- and object files.
	 */
	public static final String OBJECT_DBU_POOLTABLE_8FEET          = "models/billiard/DBUPoolTable_8Feet_01.obj";
	public static final String OBJECT_POOLCUEBALL                  = "models/billiard/poolCueBall.obj";
	public static final String OBJECT_POOLBALL01                   = "models/billiard/poolBall01.obj";
	public static final String OBJECT_POOLBALL02                   = "models/billiard/poolBall02.obj";
	public static final String OBJECT_POOLBALL03                   = "models/billiard/poolBall03.obj";
	public static final String OBJECT_POOLBALL04                   = "models/billiard/poolBall04.obj";
	public static final String OBJECT_POOLBALL05                   = "models/billiard/poolBall05.obj";
	public static final String OBJECT_POOLBALL06                   = "models/billiard/poolBall06.obj";
	public static final String OBJECT_POOLBALL07                   = "models/billiard/poolBall07.obj";
	public static final String OBJECT_POOLBALL08                   = "models/billiard/poolBall08.obj";
	public static final String OBJECT_POOLBALL09                   = "models/billiard/poolBall09.obj";
	public static final String OBJECT_POOLBALL10                   = "models/billiard/poolBall10.obj";
	public static final String OBJECT_POOLBALL11                   = "models/billiard/poolBall11.obj";
	public static final String OBJECT_POOLBALL12                   = "models/billiard/poolBall12.obj";
	public static final String OBJECT_POOLBALL13                   = "models/billiard/poolBall13.obj";
	public static final String OBJECT_POOLBALL14                   = "models/billiard/poolBall14.obj";
	public static final String OBJECT_POOLBALL15                   = "models/billiard/poolBall15.obj";
	
	/**
	 * Returns a DBU normed pool cue ball centered at (0.,0.,0.).
	 */
	public static BilliardBall getDBUPoolBalls_cueBall() {
		BilliardBall cueBall = new BilliardBall(Billiard.DBU_POOLBALL_MASS, Billiard.DBU_POOLBALL_RADIUS);
		return cueBall;
	}
	
	/**
	 * Returns a DBU normed pool cue ball centered at the given point.
	 */
	public static BilliardBall getDBUPoolBalls_cueBall(MathVector center) {
		BilliardBall cueBall = getDBUPoolBalls_cueBall();
		Physics.moveObject(cueBall, center);
		return cueBall;
	}
	
	/**
	 * Returns a DBU normed pool cue ball in standard position for an 8-feet DBU table.
	 */
	public static BilliardBall getDBUPoolBalls_cueBall_8feet() {
		BilliardBall cueBall = getDBUPoolBalls_cueBall();
		setDBUPoolBalls_cueBall_8feet(cueBall);
		return cueBall;
	}
	
	/**
	 * Returns a DBU normed pool cue ball in standard position for an 9-feet DBU table.
	 */
	public static BilliardBall getDBUPoolBalls_cueBall_9feet() {
		BilliardBall cueBall = getDBUPoolBalls_cueBall();
		setDBUPoolBalls_cueBall_9feet(cueBall);
		return cueBall;
	}
	
	/**
	 * Returns a collection of 15 DBU normed pool billiard balls in a triangular position. The first ball will be
	 * centered in (0.,radius,0.) and represents the head of the triangle. The triangle "grows" in x-direction. The
	 * numbering will correspond to the ball numbers (i.e. ball with number i will be at position i-1 in the returned
	 * collection.
	 */
	public static ArrayList<BilliardBall> getDBUPoolBalls_triangle() {
		ArrayList<BilliardBall> balls = new ArrayList<BilliardBall>(15);
		for (int i = 0; i < 15; i++)
			balls.add(new BilliardBall(DBU_POOLBALL_MASS, DBU_POOLBALL_RADIUS));
		setDBUPoolBalls_triangle(balls);
		return balls;
	}
	
	/**
	 * Returns a collection of 15 DBU normed pool billiard balls in a triangular position. The first ball will be
	 * centered in the given point and represents the head of the triangle. The triangle "grows" to the right (in
	 * x-direction).
	 */
	public static ArrayList<BilliardBall> getDBUPoolBalls_triangle(MathVector center) {
		ArrayList<BilliardBall> balls = getDBUPoolBalls_triangle();
		moveBalls(balls, center);
		return balls;
	}
	
	/**
	 * Returns a collection of 15 DBU normed pool billiard balls for an 8-feet DBU table in a triangular default
	 * position.
	 */
	public static ArrayList<BilliardBall> getDBUPoolBalls_triangle_8feet() {
		ArrayList<BilliardBall> balls = new ArrayList<BilliardBall>(15);
		for (int i = 0; i < 15; i++)
			balls.add(new BilliardBall(DBU_POOLBALL_MASS, DBU_POOLBALL_RADIUS));
		setDBUPoolBalls_triangle_8feet(balls);
		return balls;
	}
	
	/**
	 * Returns a collection of 15 DBU normed pool billiard balls for an 9-feet DBU table in a triangular default
	 * position.
	 */
	public static ArrayList<BilliardBall> getDBUPoolBalls_triangle_9feet() {
		ArrayList<BilliardBall> balls = new ArrayList<BilliardBall>(15);
		for (int i = 0; i < 15; i++)
			balls.add(new BilliardBall(DBU_POOLBALL_MASS, DBU_POOLBALL_RADIUS));
		setDBUPoolBalls_triangle_9feet(balls);
		return balls;
	}
	
	/**
	 * Moves a collection of billiard balls due to the given translation vector.
	 * 
	 * @throws IllegalArgumentException If the given translation vector does not have dimension three.
	 */
	public static void moveBalls(Collection<BilliardBall> balls, MathVector translation) {
		for (BilliardBall ball : balls)
			Physics.moveObject(ball, translation);
	}
	
	/**
	 * Sets the position of the given ball to match the start position of the cue ball on a DBU normed 8-feet billiard
	 * table.
	 */
	public static void setDBUPoolBalls_cueBall_8feet(BilliardBall cueBall) {
		MathVector center = new MathVector(3);
		center.set(1, -2 * DBU_POOLTABLE_8FEET_DIAMONDDISTANCE);
		center.set(2, DBU_POOLBALL_RADIUS);
		center.set(3, 0.);
		cueBall.setPosition(center);
	}
	
	/**
	 * Sets the position of the given ball to match the start position of the cue ball on a DBU normed 9-feet billiard
	 * table.
	 */
	public static void setDBUPoolBalls_cueBall_9feet(BilliardBall cueBall) {
		MathVector center = new MathVector(3);
		center.set(1, -2 * DBU_POOLTABLE_9FEET_DIAMONDDISTANCE);
		center.set(2, DBU_POOLBALL_RADIUS);
		center.set(3, 0.);
		cueBall.setPosition(center);
	}
	
	/**
	 * Will set the position of the given balls to match a standard triangular position. The first ball will be centered
	 * in (0.,radius,0.) and represents the head of the triangle. The triangle "grows" in x-direction. The numbering
	 * will correspond to the ball numbers (i.e. ball with number i will be at position i-1 in the returned collection.
	 * 
	 * @throws IllegalArgumentException if the ball list contains not exactly 15 balls.
	 */
	public static void setDBUPoolBalls_triangle(ArrayList<BilliardBall> balls) {
		if (balls.size() != 15)
			throw new IllegalArgumentException("Triangle position for DBUPoolBalls only for exactly 15 balls.");
		
		// compute displacements for triangle
		double buffer = 0.001;
		double displacement_x = Math.sqrt(3) * DBU_POOLBALL_RADIUS + buffer;
		double displacement_y = DBU_POOLBALL_RADIUS;
		double displacement_z = DBU_POOLBALL_RADIUS + buffer;
		
		// set ball positions (clustered in rows)
		balls.get(0).setPosition(0 * displacement_x, displacement_y, 0 * displacement_z);
		
		balls.get(13).setPosition(1 * displacement_x, displacement_y, -1 * displacement_z);
		balls.get(8).setPosition(1 * displacement_x, displacement_y, 1 * displacement_z);
		
		balls.get(5).setPosition(2 * displacement_x, displacement_y, -2 * displacement_z);
		balls.get(7).setPosition(2 * displacement_x, displacement_y, 0 * displacement_z);
		balls.get(1).setPosition(2 * displacement_x, displacement_y, 2 * displacement_z);
		
		balls.get(12).setPosition(3 * displacement_x, displacement_y, -3 * displacement_z);
		balls.get(6).setPosition(3 * displacement_x, displacement_y, -1 * displacement_z);
		balls.get(14).setPosition(3 * displacement_x, displacement_y, 1 * displacement_z);
		balls.get(9).setPosition(3 * displacement_x, displacement_y, 3 * displacement_z);
		
		balls.get(4).setPosition(4 * displacement_x, displacement_y, -4 * displacement_z);
		balls.get(11).setPosition(4 * displacement_x, displacement_y, -2 * displacement_z);
		balls.get(3).setPosition(4 * displacement_x, displacement_y, 0 * displacement_z);
		balls.get(10).setPosition(4 * displacement_x, displacement_y, 2 * displacement_z);
		balls.get(2).setPosition(4 * displacement_x, displacement_y, 4 * displacement_z);
	}
	
	/**
	 * Sets the position of the balls to match standard triangular start position on an DBU normed 8-feet table.
	 */
	public static void setDBUPoolBalls_triangle_8feet(ArrayList<BilliardBall> balls) {
		setDBUPoolBalls_triangle(balls);
		MathVector center = new MathVector(3);
		center.set(1, 2 * DBU_POOLTABLE_8FEET_DIAMONDDISTANCE);
		center.set(2, 0.);
		center.set(3, 0.);
		moveBalls(balls, center);
	}
	
	/**
	 * Sets the position of the balls to match standard triangular start position on an DBU normed 9-feet table.
	 */
	public static void setDBUPoolBalls_triangle_9feet(ArrayList<BilliardBall> balls) {
		setDBUPoolBalls_triangle(balls);
		MathVector center = new MathVector(3);
		center.set(1, 2 * DBU_POOLTABLE_9FEET_DIAMONDDISTANCE);
		center.set(2, 0.);
		center.set(3, 0.);
		moveBalls(balls, center);
	}
}
